﻿using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

namespace Ecobee.Net
{
	public class RevisionList : ApiObjectCollection<Thermostat>
	{
		public RevisionList(string propertyName)
			: base(propertyName)
		{
		}

		/// <summary>
		/// This request retrieves a list of thermostat configuration and state revisions. This request is a light-weight 
		/// polling method which will only return the revision numbers for the significant portions of the thermostat 
		/// data. It is the responsibility of the caller to store these revisions for future determination whether 
		/// changes occurred at the next poll interval.
		/// The intent is to permit the caller to determine whether a thermostat has changed since the last poll. 
		/// Retrieval of a whole thermostat including runtime data is expensive and impractical for large amounts of 
		/// thermostat such as a management set hierarchy, especially if nothing has changed. By storing the retrieved 
		/// revisions, the caller may determine whether to get a thermostat and which sections of the thermostat should 
		/// be retrieved.
		/// Revisions are UTC date/time stamps in the format: YYMMDDHHMMSS. However, due to possible time drift 
		/// between the API consumer, the server and thermostat, it is recommended that they are treated as a string, 
		/// rather than as a date/time stamp. The recommended method to test for revision changes is to simply do a 
		/// string comparison on the previous and current revision. If the strings match, nothing changed. Otherwise 
		/// request the thermostat including the relevant information which changed.
		/// </summary>
		public async Task<bool> RequestSummary(Access access, Selection selection = null)
		{
			bool returnValue = false;
			ThermostatSummary requestSummary = new ThermostatSummary();

			if (selection == null)
			{
				requestSummary.Selection = new Selection()
				{
					SelectionType = SelectionType.Registered,
					SelectionMatch = null
				};
			}
			else
			{
				requestSummary.Selection = selection;
			}

			// ***
			// *** Call the web service
			// ***
			ApiResult response = await Api.Http.Get(Api.KnownUri.RequestThermostatSummary(requestSummary), access);

			if (response.Successful)
			{
				if (response.DataObject.revisionList != null)
				{
					// ***
					// *** Parse the response
					// ***
					this.Load(response.DataObject.revisionList);
				}

				returnValue = true;
			}

			return returnValue;
		}

		public static async Task<dynamic> LoadMultipleDetails(Selection selection, Access access)
		{
			dynamic returnValue = null;

			// ***
			// *** Call the web service
			// ***
			ApiResult response = await Api.Http.Get(Api.KnownUri.Thermostat(selection), access);

			if (response.DataObject.thermostatList != null)
			{
				returnValue = response.DataObject;
			}

			return returnValue;
		}

		public async Task<bool> LoadDetails(Access access, Selection selection)
		{
			bool returnValue = false;

			// ***
			// *** Force the selection type to Thermostats
			// ***
			selection.SelectionType = SelectionType.Thermostats;

			// ***
			// *** If the SelectionMatch is empty then select ALL thermostats
			// ***
			if (string.IsNullOrWhiteSpace(selection.SelectionMatch))
			{
				selection.SelectionMatch = string.Join(",", this.Select(t => t.Identifier).ToArray());
			}

			// ***
			// *** Make the API call
			// ***
			dynamic dataObject = await Thermostat.LoadMultipleDetails(access, selection);

			if (dataObject != null)
			{
				// ***
				// *** update each thermostat
				// ***
				foreach (var item in dataObject.thermostatList)
				{
					// ***
					// *** Locate the appropriate thermostat object by Identifier
					// ***
					var qry = from tbl in this.Items
							  where tbl.Identifier == ((JValue)item.identifier).ToObject<string>()
							  select tbl;

					if (qry.Count() == 1)
					{
						// ***
						// *** Get a reference to the thermostat object'
						// *** for convenience. Note that objects are
						// *** always updated and not re-created. This 
						// *** allows the properties to raise Property
						// *** changed events so that UI bindings (if
						// *** used) are updated appropriately
						// ***
						Thermostat thermostat = qry.Single();

						// ***
						// *** Check if the Runtime data should be updated
						// ***
						if (selection.IncludeRuntime)
						{
							thermostat.Runtime.FromDataObject(item.runtime);
						}

						// ***
						// *** Check if the Settings data should be updated
						// ***
						if (selection.IncludeSettings)
						{
							thermostat.Settings.FromDataObject(item.settings);
						}

						// ***
						// ***
						// ***
						if (selection.IncludeEvents)
						{
							thermostat.Events.Load(item.events);
						}

						// ***
						// *** Load alerts
						// ***
						if (selection.IncludeAlerts)
						{
							int count = item.alerts.Count;

							if (count > 0)
							{
								thermostat.Alerts.Load(item.alerts);
							}
						}

						returnValue = true;
					}
				}
			}

			return returnValue;
		}

		protected override void OnLoad(dynamic dataObject)
		{
			foreach (var item in dataObject)
			{
				// ***
				// *** Parse the string into its parts
				// ***
				string[] values = item.ToString().Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);

				// ***
				// *** The parsed data will be assigned to a thermostat object
				// ***
				Thermostat thermostat = null;

				// ***
				// *** Check if the thermostat is already in the list
				// ***
				var existingItem = this.Where(t => t.Identifier == values[0]);

				if (existingItem.Count() == 1)
				{
					// ***
					// *** The thermostat already exists.
					// ***
					thermostat = existingItem.Single();
				}
				else
				{
					// ***
					// *** The thermostat does not exist. Create a new object
					// *** and add it to the collection.
					// ***
					thermostat = new Thermostat();
					this.Add(thermostat);
				}

				// ***
				// *** Update the details
				// ***
				thermostat.Identifier = values[0];
				thermostat.Name = values[1];
				thermostat.Connected = Convert.ToBoolean(values[2]);
				thermostat.ThermostatRevision.SetNewValue(values[3]);
				thermostat.AlertsRevision.SetNewValue(values[4]);
				thermostat.RuntimeRevision.SetNewValue(values[5]);
			}
		}
	}
}
